package no.hib.mod250.chat.client.controller;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import no.hib.mod250.chat.client.model.RetryTimer;
import no.hib.mod250.chat.client.view.View;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.Panel;

public abstract class Controller extends RetryTimer {

	// CLASSSES

	// ... TASK

	public abstract static class Task {

		public abstract void execute();

	}

	/**
	 * Provides a mechanism to perform write tasks sequentially and retry tasks
	 * that fail.
	 */
	protected class TaskQueue extends RetryTimer {

		private LinkedList<Task> queue = new LinkedList<Task>();

		public void post(Task task) {
			queue.add(task);

			// Notify status task finished
			doNotifyStatusTaskStarted(task);

			if (queue.size() == 1) {
				executeHead();
			}
		}

		protected void executeHead() {
			if (!queue.isEmpty()) {
				queue.getFirst().execute();
			}
		}

		protected void executeNext() {
			queue.removeFirst();
			executeHead();
		}

		protected void taskFailed(Task task, boolean fatal) {
			assert task == queue.getFirst();

			// Report a failure to the Model.
			onStatusServerFailed(fatal);

			// Schedule a retry.
			retryLater();
		}

		protected void taskSucceeded(Task task) {
			assert task == queue.getFirst();

			// Report a success to the Model.
			onStatusServerSucceeded();

			// Notify status task finished
			doNotifyStatusTaskFinished(task);

			// Reset the retry counter.
			resetRetryCount();

			// Move on to the next task.
			executeNext();
		}

		@Override
		protected void retry() {
			executeHead();
		}

	}

	// ... /TASK

	// /CLASSES

	// VARIABLES

	protected View view;
	protected TaskQueue taskQueue = new TaskQueue();
	protected EntryPoint entryPoint;
	protected boolean offline;
	protected Panel root;
	protected List<View.DataObserver> dataObservers = new ArrayList<View.DataObserver>();
	protected List<View.StatusObserver> statusObservers = new ArrayList<View.StatusObserver>();

	// /VARIABLES

	// CONSTRUCTOR

	public Controller(EntryPoint entryPoint, Panel root) {
		this.entryPoint = entryPoint;
		this.root = root;
	}

	// /CONSTRUCTOR

	// FUNCTIONS

	@Override
	protected void retry() {
		doLoad();
	}

	// ... ADD

	public void addDataObserver(View.DataObserver dataObserver) {
		this.dataObservers.add(dataObserver);
	}

	public void addStatusObserver(View.StatusObserver statusObserver) {
		this.statusObservers.add(statusObserver);
	}

	// ... /ADD

	// ... DO

	public abstract void doLoad();

	// ... ... NOTIFY

	// ... ... ... STATUS

	public void doNotifyStatusServerCameBack() {
		for (View.StatusObserver stausObserver : statusObservers) {
			stausObserver.onStatusServerCameBack();
		}
	}

	public void doNotifyStatusServerWentAway() {
		for (View.StatusObserver stausObserver : statusObservers) {
			stausObserver.onStatusServerWentAway();
		}
	}

	public void doNotifyStatusTaskStarted(Task task) {
		for (View.StatusObserver stausObserver : statusObservers) {
			stausObserver.onStatusTaskStarted(task);
		}
	}

	public void doNotifyStatusTaskFinished(Task task) {
		for (View.StatusObserver stausObserver : statusObservers) {
			stausObserver.onStatusTaskFinished(task);
		}
	}

	// ... ... ... /STATUS

	// ... ... /NOTIFY

	// ... /DO

	// ... ON

	public abstract void onControllerLoaded();

	public abstract void onControllerLoadFailed();

	/**
	 * Invoked by tasks and loaders when RPC invocations begin to fail.
	 */
	public void onStatusServerFailed(boolean fatal) {
		if (fatal) {
			return;
		}

		if (!offline) {
			doNotifyStatusServerWentAway();
			offline = true;
		}
	}

	/**
	 * Invoked by tasks and loaders when RPC invocations succeed.
	 */
	public void onStatusServerSucceeded() {
		if (offline) {
			doNotifyStatusServerCameBack();
			offline = false;
		}
	}

	// ... /ON

	// /FUNCTIONS

}
